package cn.virens.common.mapper.mybatis;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

import cn.hutool.core.collection.CollUtil;
import cn.virens.common.entity.BaseModel;
import cn.virens.common.exception.APIException;

/**
 * Mybatis 拦截器，填充create_time/update_time
 *
 * @author virens
 */
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class ModelOperTimeInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement statement = getMappedStatement(invocation);
        LocalDateTime dateTime = LocalDateTime.now();

        // 获取全部基于BaseModel的实体对象，并赋值更新/创建时间
        for (BaseModel obj : getModelList(invocation)) {
            if (eq(statement, SqlCommandType.INSERT)) {
                if (obj.getUpdateTime() == null) {
                    obj.setUpdateTime(dateTime);
                }

                if (obj.getCreateTime() == null) {
                    obj.setCreateTime(dateTime);
                }
            }

            if (eq(statement, SqlCommandType.UPDATE)) {
                if (obj.getUpdateTime() == null) {
                    obj.setUpdateTime(dateTime);
                }
            }
        }

        return invocation.proceed();
    }

    /**
     * 判断SQL类型
     *
     * @param stm  MappedStatement
     * @param type SQL类型
     * @return
     */
    private static boolean eq(MappedStatement stm, SqlCommandType type) {
        return stm != null && type.equals(stm.getSqlCommandType());
    }

    /**
     * 获取对应的数据实体对象
     *
     * @param invocation Invocation
     * @return 实体列表
     */
    private static Collection<BaseModel> getModelList(Invocation invocation) {
        Collection<BaseModel> answer = new HashSet<>();

        for (Object item : invocation.getArgs()) {
            if (item instanceof Collection<?>) {
                addColl(answer, (Collection<?>) item);
            } else if (item instanceof Map<?, ?>) {
                addMap(answer, (Map<?, ?>) item);
            } else {
                add(answer, item);
            }
        }

        return answer;
    }

    private static boolean add(Collection<BaseModel> coll, Object obj) {
        if (obj != null && obj instanceof BaseModel) {
            return coll.add((BaseModel) obj);
        } else {
            return false;
        }
    }

    private static void addMap(Collection<BaseModel> coll, Map<?, ?> obj) {
        CollUtil.forEach(obj.values(), (a, i) -> add(coll, a));
    }

    private static void addColl(Collection<BaseModel> coll, Collection<?> obj) {
        CollUtil.forEach(obj, (a, i) -> add(coll, a));
    }

    /**
     * 获取对应的SQL实体对象
     *
     * @param invocation Invocation
     * @return 实体对象
     * @throws APIException 一般异常
     */
    private static MappedStatement getMappedStatement(Invocation invocation) throws APIException {
        for (Object obj : invocation.getArgs()) {
            if (obj instanceof MappedStatement) {
                return (MappedStatement) obj;//
            }
        }

        return null;
    }

}
